@bridge-ds/core 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +17 -0
  2. package/bridge.config.schema.json +21 -0
  3. package/dist/audit/index.js +3 -3
  4. package/dist/audit/report.d.ts +17 -2
  5. package/dist/branch-actions.d.ts +7 -1
  6. package/dist/build-requests-github.d.ts +25 -0
  7. package/dist/build-requests.d.ts +403 -0
  8. package/dist/changelog-model.d.ts +102 -0
  9. package/dist/component-spec.d.ts +3 -1
  10. package/dist/config.d.ts +26 -0
  11. package/dist/css-color.d.ts +25 -0
  12. package/dist/docs-proposal.d.ts +1 -0
  13. package/dist/explorations.d.ts +1 -0
  14. package/dist/fidelity-references.d.ts +12 -0
  15. package/dist/fidelity.d.ts +12 -1
  16. package/dist/figma-fix-loop.d.ts +153 -0
  17. package/dist/figma-fix-run.d.ts +103 -0
  18. package/dist/figma-fixes.d.ts +169 -0
  19. package/dist/figma-push-github.d.ts +7 -2
  20. package/dist/figma-push.d.ts +15 -2
  21. package/dist/gap-case.d.ts +143 -0
  22. package/dist/github-client.d.ts +31 -0
  23. package/dist/id-map.d.ts +10 -0
  24. package/dist/index.d.ts +17 -0
  25. package/dist/index.js +3996 -818
  26. package/dist/index.js.map +1 -1
  27. package/dist/layered-review.d.ts +2 -0
  28. package/dist/measure-location.d.ts +42 -0
  29. package/dist/measure-status.d.ts +127 -0
  30. package/dist/measure-workflow.d.ts +48 -0
  31. package/dist/measured-only.d.ts +30 -0
  32. package/dist/measured-spec.d.ts +349 -0
  33. package/dist/measured-tokens.d.ts +25 -0
  34. package/dist/motion-docs.d.ts +51 -0
  35. package/dist/node/audit.d.ts +11 -3
  36. package/dist/node/index.d.ts +1 -0
  37. package/dist/node/index.js +1640 -24
  38. package/dist/node/index.js.map +1 -1
  39. package/dist/node/measure-script.d.ts +1 -0
  40. package/dist/node/measure.d.ts +212 -0
  41. package/dist/node/spec-css.d.ts +9 -1
  42. package/dist/node/tokens-script.d.ts +3 -0
  43. package/dist/pr-composer.d.ts +5 -0
  44. package/dist/previews.d.ts +4 -1
  45. package/dist/{prod-branch-DenQkr_e.js → prod-branch-CJZwMfVD.js} +223 -183
  46. package/dist/prod-branch-CJZwMfVD.js.map +1 -0
  47. package/dist/prod-branch.d.ts +1 -1
  48. package/dist/repo-model.d.ts +7 -1
  49. package/dist/{repo-source-CRfhLO87.js → repo-source-CshjjC7x.js} +1485 -629
  50. package/dist/repo-source-CshjjC7x.js.map +1 -0
  51. package/dist/repo-source-github.d.ts +8 -0
  52. package/dist/repo-source.d.ts +6 -0
  53. package/dist/{report-DVEmUr0p.js → report-CE-jHAuw.js} +17 -6
  54. package/dist/{report-DVEmUr0p.js.map → report-CE-jHAuw.js.map} +1 -1
  55. package/dist/requester.d.ts +14 -0
  56. package/dist/requests.d.ts +2 -0
  57. package/dist/review-gate.d.ts +189 -2
  58. package/dist/risk.d.ts +12 -0
  59. package/dist/share-branch.d.ts +1 -1
  60. package/dist/snapshot.d.ts +34 -0
  61. package/dist/suggestion-review.d.ts +45 -2
  62. package/dist/suggestions-github.d.ts +33 -1
  63. package/dist/suggestions.d.ts +6 -0
  64. package/dist/tier1-publish.d.ts +48 -0
  65. package/dist/token-proposal.d.ts +7 -0
  66. package/dist/try-in-product.d.ts +9 -0
  67. package/package.json +1 -1
  68. package/dist/prod-branch-DenQkr_e.js.map +0 -1
  69. package/dist/repo-source-CRfhLO87.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Florian Gouloubi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation
7
+ the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
8
+ to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of
11
+ the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
14
+ THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
16
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
17
+ IN THE SOFTWARE.
@@ -13,6 +13,12 @@
13
13
  "const": 1,
14
14
  "description": "Config format version."
15
15
  },
16
+ "name": {
17
+ "type": "string",
18
+ "minLength": 1,
19
+ "maxLength": 60,
20
+ "description": "The team's display name, shown in Studio, the Library and invitations (\"Acme Design\"). Absent: Bridge names the team after its repository."
21
+ },
16
22
  "repo": {
17
23
  "description": "GitHub repository. Pull requests target defaultBranch; Bridge never writes to it.",
18
24
  "oneOf": [
@@ -452,6 +458,11 @@
452
458
  "description": "For a design system run by one person. When true and the owners resolve to exactly one person who holds both roles, that person's approval counts as the design and the tech approval, so Tier 2 and Tier 3 changes can be approved. The author of a change still never approves it. Default false: one person never covers both approvals alone.",
453
459
  "default": false
454
460
  },
461
+ "leadsPublishTier1": {
462
+ "type": "boolean",
463
+ "description": "Leads can publish small changes without review. When true (or absent), team admins and the reviewers of the matching domain (design for docs and guidance, tech for code-only Tier 1) publish a Tier 1 change directly: Bridge opens the pull request, waits for the checks and merges it, recording who published it. A failing check sends it to review instead. Tier 2 and 3 are unchanged. Changing it is a Tier 3 change (bridge.config.json is a guardrail file).",
464
+ "default": true
465
+ },
455
466
  "rights": {
456
467
  "type": "object",
457
468
  "additionalProperties": false,
@@ -700,6 +711,16 @@
700
711
  }
701
712
  }
702
713
  },
714
+ "demo": {
715
+ "type": "object",
716
+ "additionalProperties": false,
717
+ "required": ["viewer"],
718
+ "description": "A public demo's persona, read by Bridge's own demo repositories only: who a visitor without an account stands in for on the demo home (\"Good morning, Dana.\"). Ignored on any other repository.",
719
+ "properties": {
720
+ "viewer": { "type": "string", "minLength": 1, "description": "The persona's first name, for example \"Dana\"." },
721
+ "role": { "type": "string", "description": "What they do, for example \"DS lead\"." }
722
+ }
723
+ },
703
724
  "visualImpact": {
704
725
  "type": "object",
705
726
  "additionalProperties": false,
@@ -1,6 +1,6 @@
1
- import { i as isAuditable, a as auditFile } from "../report-DVEmUr0p.js";
2
- import { A, b, c, C, D, d, F, I, L, e, M, N, f, g, h, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, B, E, G, H, J, K, O, P, Q, R, S, T, U, V, W, X, Y, Z } from "../report-DVEmUr0p.js";
3
- import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-DenQkr_e.js";
1
+ import { i as isAuditable, a as auditFile } from "../report-CE-jHAuw.js";
2
+ import { A, b, c, C, D, d, F, I, L, e, M, N, f, g, h, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, B, E, G, H, J, K, O, P, Q, R, S, T, U, V, W, X, Y, Z } from "../report-CE-jHAuw.js";
3
+ import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-CJZwMfVD.js";
4
4
  const lines = (text) => text.split(/\r?\n/);
5
5
  function lineBag(text) {
6
6
  const bag = /* @__PURE__ */ new Map();
@@ -44,8 +44,23 @@ export interface CssFixEdit {
44
44
  to: string;
45
45
  token: string;
46
46
  }
47
- /** Safe autofix for a CSS/SCSS/Less file: exact token matches become var(--token). Returns the edits and the new text. */
48
- export declare function planCssFixes(text: string, lang: 'css' | 'scss' | 'less', ds: DesignSystemIndex): {
47
+ /** A value the fix leaves as it is, like the audit leaves it out of the counts: an ignore comment or the allowlist. */
48
+ export interface CssFixKept {
49
+ line: number;
50
+ column: number;
51
+ from: string;
52
+ reason: string;
53
+ via: 'comment' | 'allowlist';
54
+ }
55
+ /**
56
+ * Safe autofix for a CSS/SCSS/Less file: exact token matches become var(--token). Returns the edits and the new text.
57
+ * The same exceptions as the audit: a `bridge-audit-ignore-next-line: reason` comment on the line above, and the
58
+ * allowlist entries of this file, keep the value (listed in `kept`).
59
+ */
60
+ export declare function planCssFixes(text: string, lang: 'css' | 'scss' | 'less', ds: DesignSystemIndex, opts?: {
61
+ allowlist?: AllowlistEntry[] | undefined;
62
+ }): {
49
63
  edits: CssFixEdit[];
50
64
  output: string;
65
+ kept: CssFixKept[];
51
66
  };
@@ -1,10 +1,15 @@
1
1
  import type { GitHubClient, Review } from './github-client';
2
2
  import { type MergeMethod, type MissingApproval } from './review-gate';
3
- /** Merges a branch at the head the person saw (GitHub refuses when it moved meanwhile). Studio's Merge button: squash. */
3
+ /**
4
+ * Merges a branch at the head the person saw (GitHub refuses when it moved meanwhile). Studio's Merge button: squash.
5
+ * `draft`: GitHub still calls it a draft though the gate reads it ready (core isDraft, `drafted-by-claude`), so it is
6
+ * marked ready for review first: GitHub never merges a draft.
7
+ */
4
8
  export declare function mergeOnGitHub(gh: GitHubClient, input: {
5
9
  number: number;
6
10
  headSha: string;
7
11
  method?: MergeMethod | undefined;
12
+ draft?: boolean | undefined;
8
13
  }): Promise<{
9
14
  sha: string;
10
15
  }>;
@@ -28,6 +33,7 @@ export declare function overrideMergeOnGitHub(gh: GitHubClient, input: {
28
33
  reason: string;
29
34
  missing: MissingApproval[];
30
35
  method: MergeMethod;
36
+ draft?: boolean | undefined;
31
37
  }): Promise<{
32
38
  sha: string;
33
39
  commentUrl: string;
@@ -0,0 +1,25 @@
1
+ import { type BuildRequestInput, type BuildRequestView, type LibraryArrival, type PublishOutcome } from './build-requests';
2
+ import type { GitHubClient } from './github-client';
3
+ /**
4
+ * Files a build request: one issue, labeled component-change (the bridge-jira Action mirrors it to Jira when set up),
5
+ * bridge-build, the request kind and the source. `labels` and `assignees` come from integrations.requests. Returns the
6
+ * outcome the plugin shows ("Build request sent").
7
+ */
8
+ export declare function createBuildRequestOnGitHub(gh: GitHubClient, input: BuildRequestInput, opts?: {
9
+ labels?: string[] | undefined;
10
+ assignees?: string[] | undefined;
11
+ componentUrl?: string | undefined;
12
+ }): Promise<PublishOutcome>;
13
+ /**
14
+ * Every build request with its status, read from GitHub: the component-change and bridge-build issues, and the pull
15
+ * requests that close them or are library branches from Figma. Two to three list calls, no per-item reads.
16
+ */
17
+ export declare function listBuildRequestsOnGitHub(gh: GitHubClient, opts?: {
18
+ now?: Date | number | undefined;
19
+ nameOf?: ((login: string) => string) | undefined;
20
+ }): Promise<BuildRequestView[]>;
21
+ /**
22
+ * Where a token push the plugin published as `shipped` stands: poll it until `final` ("In the library", or "Held for a
23
+ * developer" / "Not shipped"). Reads the pull request, its checks and its reviews.
24
+ */
25
+ export declare function libraryArrivalOnGitHub(gh: GitHubClient, n: number): Promise<LibraryArrival>;
@@ -0,0 +1,403 @@
1
+ import { type BridgeConfig } from './config';
2
+ import { type ComponentChangeRequest, type RequestIssue, type RequestKind } from './requests';
3
+ import { type ChangedFile, type TokenChangeKind, type TokenChangeSummary } from './risk';
4
+ import { type CiSummary } from './review-gate';
5
+ /** Every build request issue carries this label (with component-change, which the Jira Action mirrors). */
6
+ export declare const BUILD_REQUEST_LABEL = "bridge-build";
7
+ /** The label the plugin puts on what it pushes (token pull requests, suggestions and requests). */
8
+ export declare const SOURCE_FIGMA_LABEL = "source: figma";
9
+ /** A build request waits this many days before it reads as stale. */
10
+ export declare const BUILD_STALE_DAYS = 7;
11
+ /**
12
+ * What the two images of a build request are called on designer screens (Studio): the component as it is in code
13
+ * now, and the design in Figma. The data keeps its field names, `before` and `after`; the GitHub issue table, read by
14
+ * developers, keeps Before and After.
15
+ */
16
+ export declare const BUILD_IMAGE_LABEL: {
17
+ readonly before: "In code now";
18
+ readonly after: "In Figma";
19
+ };
20
+ /**
21
+ * Whether a pull request (or issue) was pushed from the Figma plugin: the `source: figma` or `from-figma` label, or the
22
+ * hidden "From Figma" origin block of a component branch.
23
+ */
24
+ export declare function isFigmaPush(p: {
25
+ labels?: readonly ({
26
+ name?: string;
27
+ } | string)[] | null | undefined;
28
+ body?: string | null | undefined;
29
+ }): boolean;
30
+ /**
31
+ * What a push from Figma changes, from the pull request's real files and token changes (never from what its body says):
32
+ * - `token-values`: token files only (values changed or tokens added), plus changesets, docs and Bridge's own files;
33
+ * - `token-structure`: a token renamed or removed;
34
+ * - `token-unknown`: token files changed but the token changes were not read (counts like token-structure);
35
+ * - `component`: component or other source code (a Claude draft branch, the pull request of a build request);
36
+ * - `outside`: a guardrail file (CI, AGENTS.md, bridge.config.json, contrast checks): no Figma credit at all.
37
+ */
38
+ export type FigmaPushScope = 'token-values' | 'token-structure' | 'token-unknown' | 'component' | 'outside';
39
+ /** A token file: `*.tokens.json`, or a JSON file under `tokens.root`. */
40
+ export declare function isTokenPath(path: string, config?: BridgeConfig | undefined): boolean;
41
+ export declare function figmaPushScope(input: {
42
+ files: readonly ChangedFile[];
43
+ tokenChanges: readonly {
44
+ kind: TokenChangeKind | string;
45
+ }[] | null | undefined;
46
+ config?: BridgeConfig | undefined;
47
+ }): FigmaPushScope;
48
+ /**
49
+ * Whether the token files of a pull request are worth reading to tell a token value change (isTokenValueChange): it
50
+ * changes token files, and nothing beside them but changesets, docs and Bridge's own files. Every other pull request
51
+ * (code, a guardrail file, no token file) is not one, with no read.
52
+ */
53
+ export declare function mayBeTokenValueChange(files: readonly ChangedFile[], config?: BridgeConfig | undefined): boolean;
54
+ /**
55
+ * Decision TV-1 (docs/BUILD-REQUESTS.md, "Token values from Studio"): a pull request that only changes token values
56
+ * (values changed or tokens added, from its real files and token changes, never from its body), whoever opened it.
57
+ * Once the design approval is given it ships after the automatic checks, with no developer code OK (review-gate
58
+ * withTokenValueApprovals, branchVerdict `tokenValues`). A rename or removal, unread token changes (null), code, a
59
+ * guardrail file or a running exploration is not one: those still need the tech approval.
60
+ */
61
+ export declare function isTokenValueChange(input: {
62
+ files: readonly ChangedFile[];
63
+ tokenChanges: readonly {
64
+ kind: TokenChangeKind | string;
65
+ }[] | null | undefined;
66
+ config?: BridgeConfig | undefined;
67
+ labels?: readonly ({
68
+ name?: string;
69
+ } | string)[] | null | undefined;
70
+ }): boolean;
71
+ /**
72
+ * The token changes between two versions of token files, by kind, from their text alone (DTCG: a node with `$value` is a
73
+ * token, its path the keys joined by "."). For callers that only have the files of a pull request (the server arming a
74
+ * merge, Studio's branch page): a path gone is `removed` (a rename reads as removed plus added, which gates the same),
75
+ * a new path `added`, a different `$value`, `$type` or extension `changed`, a different `$description` only
76
+ * `changed` with descriptionOnly. Unreadable JSON throws, so the caller treats the changes as unknown.
77
+ */
78
+ export declare function tokenFileChanges(files: readonly {
79
+ before: string | null | undefined;
80
+ after: string | null | undefined;
81
+ }[]): TokenChangeSummary[];
82
+ /** The Figma push facts the gate reads (branchVerdict `figmaPush`). Null when the pull request is not one. */
83
+ export interface FigmaPush {
84
+ scope: FigmaPushScope;
85
+ }
86
+ /**
87
+ * The Figma push facts of a pull request: pushed from the plugin (isFigmaPush), or the pull request of a build request
88
+ * sent from Figma (`closesFigmaBuildRequest`: its body closes one). Null otherwise, and for a running exploration.
89
+ */
90
+ export declare function figmaPushOf(pr: {
91
+ labels?: readonly ({
92
+ name?: string;
93
+ } | string)[] | null | undefined;
94
+ body?: string | null | undefined;
95
+ }, facts: {
96
+ files: readonly ChangedFile[];
97
+ tokenChanges: readonly {
98
+ kind: TokenChangeKind | string;
99
+ }[] | null | undefined;
100
+ config?: BridgeConfig | undefined;
101
+ closesFigmaBuildRequest?: boolean | undefined;
102
+ }): FigmaPush | null;
103
+ /** Whether a scope ships with no person in the loop: design approved by the push, tech approved by being values only. */
104
+ export declare const shipsByItself: (scope: FigmaPushScope | null | undefined) => boolean;
105
+ /**
106
+ * Where one published thing went:
107
+ * - `shipped`: a token pull request with values only. Bridge merges it once the automatic checks pass (auto-merge
108
+ * armed); poll libraryArrival until it reads "In the library".
109
+ * - `waiting-for-developer`: a pull request a developer approves first (a token renamed or removed).
110
+ * - `build-request`: an issue developers build from (a structure change).
111
+ */
112
+ export type PublishRoute = 'shipped' | 'waiting-for-developer' | 'build-request';
113
+ export interface PublishOutcome {
114
+ /** What was published: a token path, or a component name. */
115
+ subject: {
116
+ type: 'token' | 'component';
117
+ name: string;
118
+ };
119
+ route: PublishRoute;
120
+ /** The pull request (shipped, waiting-for-developer) or the issue (build-request). */
121
+ ref: {
122
+ kind: 'pull' | 'issue';
123
+ number: number;
124
+ url: string;
125
+ };
126
+ /**
127
+ * `shipped` only: whether Bridge armed the merge (auto-merge on GitHub, or the Bridge app merging on green checks).
128
+ * False when it could not (no Bridge app on the repository, auto-merge off): the route is then reported as
129
+ * `waiting-for-developer` instead, so this is true on every `shipped` outcome.
130
+ */
131
+ armed?: boolean | undefined;
132
+ }
133
+ export interface PublishResult {
134
+ outcomes: PublishOutcome[];
135
+ }
136
+ export declare const PUBLISH_ROUTE_COPY: Record<PublishRoute, {
137
+ label: string;
138
+ sentence: string;
139
+ }>;
140
+ /**
141
+ * The route of a token push, from its token changes: values changed or tokens added ship; a rename or a removal waits
142
+ * for a developer. `armed` false (Bridge could not arm the merge) also waits for a developer.
143
+ */
144
+ export declare function tokenPublishRoute(changes: readonly {
145
+ kind: TokenChangeKind | string;
146
+ }[], armed?: boolean): Exclude<PublishRoute, 'build-request'>;
147
+ /** One sentence for the end of a publish: "2 on their way to the library, 1 waiting for a developer, 1 build request sent." */
148
+ export declare function publishSummary(r: PublishResult): string;
149
+ export type LibraryArrivalKey = 'on-its-way' | 'in-the-library' | 'held' | 'not-shipped';
150
+ export interface LibraryArrival {
151
+ key: LibraryArrivalKey;
152
+ label: string;
153
+ sentence: string;
154
+ /** Stop polling: it is in the library, or it will not get there by itself. */
155
+ final: boolean;
156
+ }
157
+ /**
158
+ * Where a published pull request stands, for the plugin to poll after `shipped` (every 20 to 30 seconds is plenty)
159
+ * and for Studio. Never names a technical blocker: a failing check, a conflict or a change request all read "Held for a
160
+ * developer". `held` is final for polling on a conflict or a change request (a person picks it up from there), not on a
161
+ * failed check, which a re-run can clear.
162
+ */
163
+ export declare function libraryArrival(p: {
164
+ state: string;
165
+ merged_at?: string | null | undefined;
166
+ merged?: boolean | undefined;
167
+ mergeable?: boolean | null | undefined;
168
+ ci?: CiSummary | null | undefined;
169
+ changesRequested?: number | undefined;
170
+ }): LibraryArrival;
171
+ /** The review Bridge's app leaves on a token push it ships (the merge rule's record on GitHub). */
172
+ export declare const FIGMA_SHIP_HEADING = "**Bridge: shipped from Figma**";
173
+ export declare function figmaShipReviewBody(by?: string | null): string;
174
+ /** A prop (option) the build needs. */
175
+ export interface BuildProp {
176
+ name: string;
177
+ /** "variant", "boolean", "text", "instance swap", or a TypeScript type. */
178
+ type?: string | undefined;
179
+ values?: string[] | undefined;
180
+ default?: string | undefined;
181
+ /** "new", "changed" or "removed"; default new. */
182
+ change?: 'new' | 'changed' | 'removed' | undefined;
183
+ note?: string | undefined;
184
+ }
185
+ /** What a build request adds to a component change request (kept in the request's hidden block, under `build`). */
186
+ export interface BuildRequestExtra {
187
+ v: 1;
188
+ /** Image URLs of the design before and after the change. */
189
+ before?: string[] | undefined;
190
+ after?: string[] | undefined;
191
+ props?: BuildProp[] | undefined;
192
+ /** Where the full spec lives, when it is not in the issue (too large): a path on a branch, or a URL. */
193
+ specPath?: string | undefined;
194
+ }
195
+ /** The input of the build request issue builder (the Figma plugin, Studio, the CLI and the MCP server). */
196
+ export interface BuildRequestInput {
197
+ /** Coded name ("Button"), or the proposed name of a new component. */
198
+ component: string;
199
+ kind: RequestKind;
200
+ /** One line, sentence case: "Add a lg size". */
201
+ title: string;
202
+ /** What and why, in the designer's words. */
203
+ summary: string;
204
+ /** What changes for developers in one sentence. */
205
+ line?: string | undefined;
206
+ changes?: ComponentChangeRequest['changes'] | undefined;
207
+ figma: {
208
+ fileKey?: string | undefined;
209
+ nodeId?: string | undefined;
210
+ nodeName?: string | undefined;
211
+ url?: string | undefined;
212
+ };
213
+ images?: {
214
+ before?: string[] | undefined;
215
+ after?: string[] | undefined;
216
+ } | undefined;
217
+ props?: BuildProp[] | undefined;
218
+ /** The structured spec (the plugin's ComponentPushDiff, with its token edits), shown collapsed. Omitted when too large. */
219
+ spec?: unknown;
220
+ specPath?: string | undefined;
221
+ /** A brief for Claude written by the caller; default buildRequestBrief. */
222
+ brief?: string | undefined;
223
+ source: ComponentChangeRequest['source'];
224
+ /** GitHub login of who pushed, when known (the "Requested by" record). */
225
+ requestedBy?: string | undefined;
226
+ }
227
+ /** Kinds that are structure changes, so a push from Figma becomes a build request. */
228
+ export declare const BUILD_KINDS: readonly RequestKind[];
229
+ /** The largest spec JSON kept in the issue body (GitHub caps a body at 65,536 characters). */
230
+ export declare const BUILD_SPEC_MAX = 30000;
231
+ /** The request a build request input stands for (what parseRequestIssue reads back, with `build`). */
232
+ export declare function buildRequestOf(input: BuildRequestInput): ComponentChangeRequest & {
233
+ build: BuildRequestExtra;
234
+ };
235
+ /**
236
+ * The brief Claude gets to build it ("Copy brief for Claude" in Studio, `bridge build show` in the CLI,
237
+ * `bridge_get_build_request` in the MCP server). Without an issue number (inside the issue body), it says "this issue".
238
+ */
239
+ export declare function buildRequestBrief(r: ComponentChangeRequest & {
240
+ build?: BuildRequestExtra | undefined;
241
+ }, ctx?: {
242
+ issue?: number | undefined;
243
+ issueUrl?: string | undefined;
244
+ componentsDir?: string | undefined;
245
+ agentsFile?: string | undefined;
246
+ files?: string[] | undefined;
247
+ }): string;
248
+ /**
249
+ * The GitHub issue of a build request: a component change request issue (component-change, so the bridge-jira Action
250
+ * mirrors it to Jira) with the bridge-build label, before and after images side by side, the props needed, the spec
251
+ * collapsed and the brief for Claude collapsed. parseBuildRequestIssue reads it back.
252
+ */
253
+ export declare function composeBuildRequestIssue(input: BuildRequestInput, opts?: {
254
+ labels?: string[] | undefined;
255
+ assignees?: string[] | undefined;
256
+ componentUrl?: string | undefined;
257
+ }): RequestIssue;
258
+ /** The build request in an issue body, or undefined when it has no Bridge request block. `build` is empty for a plain request. */
259
+ export declare function parseBuildRequestIssue(body: string | null | undefined): (ComponentChangeRequest & {
260
+ build: BuildRequestExtra;
261
+ }) | undefined;
262
+ /** The spec kept collapsed in a build request issue, or undefined. */
263
+ export declare function parseBuildSpec(body: string | null | undefined): unknown;
264
+ /** Whether an issue is a build request: every component change request is one (bridge-build marks the new ones). Explorations are not. */
265
+ export declare function isBuildRequestIssue(labels: readonly ({
266
+ name?: string;
267
+ } | string)[] | null | undefined): boolean;
268
+ /**
269
+ * Requested (nobody assigned) -> Picked up (assigned: "Ana is on it") -> In progress (a linked draft pull request, or a
270
+ * linked branch with commits) -> In review (a linked pull request ready for review) -> Shipped (merged, or the issue
271
+ * closed as completed). `not-planned`: closed as not planned.
272
+ */
273
+ export type BuildStatus = 'requested' | 'picked-up' | 'in-progress' | 'in-review' | 'shipped' | 'not-planned';
274
+ export declare const BUILD_STATUS_LABEL: Record<BuildStatus, string>;
275
+ /** Steps of the status timeline, in order (not-planned is off the line). */
276
+ export declare const BUILD_STEPS: readonly BuildStatus[];
277
+ /** A pull request linked to a build request (closes it, or the build request is that branch). */
278
+ export interface BuildPullLike {
279
+ number: number;
280
+ url?: string | undefined;
281
+ /** "open", "closed" or "merged" (PullRequestSummary), or GitHub's open/closed with merged_at. */
282
+ state: string;
283
+ draft?: boolean | undefined;
284
+ mergedAt?: string | null | undefined;
285
+ merged_at?: string | null | undefined;
286
+ labels?: readonly ({
287
+ name?: string;
288
+ } | string)[] | undefined;
289
+ author?: string | undefined;
290
+ assignees?: string[] | undefined;
291
+ createdAt?: string | undefined;
292
+ updatedAt?: string | undefined;
293
+ body?: string | undefined;
294
+ /** Commits on the branch, when known (a branch with only the spec commit is not work in progress). */
295
+ commits?: number | null | undefined;
296
+ headRef?: string | undefined;
297
+ title?: string | undefined;
298
+ }
299
+ export interface BuildIssueLike {
300
+ number: number;
301
+ url: string;
302
+ title: string;
303
+ body: string;
304
+ state: 'open' | 'closed';
305
+ stateReason?: string | null | undefined;
306
+ labels: readonly ({
307
+ name?: string;
308
+ } | string)[];
309
+ author?: string | undefined;
310
+ assignees?: string[] | undefined;
311
+ createdAt: string;
312
+ closedAt?: string | null | undefined;
313
+ }
314
+ export interface BuildStatusEvent {
315
+ status: BuildStatus;
316
+ /** ISO date, when GitHub says. */
317
+ at?: string | undefined;
318
+ /** Who, when it is a person ("ana"). */
319
+ who?: string | undefined;
320
+ }
321
+ export interface BuildRequestView {
322
+ /** "issue-12" or "pull-34". */
323
+ id: string;
324
+ /** Where it lives: an issue, or (direct mode, no issue) the library branch from Figma waiting for a developer. */
325
+ kind: 'issue' | 'pull';
326
+ number: number;
327
+ url: string;
328
+ component: string;
329
+ /** "Add a lg size": the request's title, without the component prefix. */
330
+ title: string;
331
+ requestKind: RequestKind | null;
332
+ status: BuildStatus;
333
+ label: string;
334
+ /** One designer sentence: "Ana is on it.", "Waiting for a developer for 9 days." */
335
+ sentence: string;
336
+ /** Who works on it: the assignees, else the author of the linked pull request in progress. */
337
+ builders: string[];
338
+ requestedBy: string | null;
339
+ createdAt: string;
340
+ ageDays: number;
341
+ /** Requested for more than BUILD_STALE_DAYS days. */
342
+ stale: boolean;
343
+ shippedAt: string | null;
344
+ /** The linked pull request that moves it furthest. */
345
+ pull: {
346
+ number: number;
347
+ url?: string | undefined;
348
+ state: string;
349
+ } | null;
350
+ /** Jira key from the bridge-jira Action's `jira: DS-12` label. */
351
+ jira: string | null;
352
+ figmaUrl: string | null;
353
+ /** The status timeline: each step reached, with when and who when GitHub says. */
354
+ timeline: BuildStatusEvent[];
355
+ }
356
+ /** The issue numbers a pull request links: "Closes #n" in its body, plus GitHub's linked issues when the caller read them. */
357
+ export declare function linkedIssueNumbers(p: Pick<BuildPullLike, 'body'>, extra?: readonly number[]): number[];
358
+ /** The Jira key the bridge-jira Action labelled an issue with ("jira: DS-12"), or null. */
359
+ export declare function jiraKeyOfLabels(labels: readonly ({
360
+ name?: string;
361
+ } | string)[] | null | undefined): string | null;
362
+ /**
363
+ * The status of one build request, from GitHub only: the issue (state, reason, assignees) and the pull requests linked
364
+ * to it. `nameOf` turns a login into the name a designer knows (team members), default the login.
365
+ */
366
+ export declare function buildRequestStatus(x: {
367
+ issue?: BuildIssueLike | null | undefined;
368
+ pull?: BuildPullLike | null | undefined;
369
+ linked?: readonly BuildPullLike[] | undefined;
370
+ }, now?: Date | number, nameOf?: (login: string) => string): Pick<BuildRequestView, 'status' | 'label' | 'sentence' | 'builders' | 'ageDays' | 'stale' | 'shippedAt' | 'pull' | 'timeline' | 'createdAt'>;
371
+ /**
372
+ * Every build request, from the issues and pull requests read on GitHub: issues that are build requests
373
+ * (isBuildRequestIssue) with the pull requests that close them, plus library branches from Figma waiting for a
374
+ * developer that no issue covers (direct mode). `linkedByIssue`: GitHub's linked pull requests per issue number, when the
375
+ * caller read the timeline (Development links), on top of "Closes #n".
376
+ */
377
+ export declare function assembleBuildRequests(input: {
378
+ issues: readonly BuildIssueLike[];
379
+ pulls: readonly BuildPullLike[];
380
+ now?: Date | number | undefined;
381
+ nameOf?: ((login: string) => string) | undefined;
382
+ linkedByIssue?: ReadonlyMap<number, readonly number[]> | undefined;
383
+ }): BuildRequestView[];
384
+ /** The filters of the Being built page. */
385
+ export type BuildFilter = 'all' | 'waiting' | 'in-progress' | 'shipped-this-week';
386
+ export declare const BUILD_FILTER_LABEL: Record<BuildFilter, string>;
387
+ /** Whether a build request shows under a filter. "All" leaves out what was closed as not planned more than a week ago. */
388
+ export declare function matchesBuildFilter(v: Pick<BuildRequestView, 'status' | 'shippedAt' | 'timeline'>, f: BuildFilter, now?: Date | number): boolean;
389
+ /** Build requests still open (not shipped, not closed). */
390
+ export declare const openBuildRequests: <V extends Pick<BuildRequestView, "status">>(views: readonly V[]) => V[];
391
+ /** The calm summary line of the home: "3 being built, 1 waiting to be picked up." Null when nothing is open. */
392
+ export declare function buildSummaryLine(views: readonly Pick<BuildRequestView, 'status' | 'stale'>[]): string | null;
393
+ /**
394
+ * The Library component page line while build requests for a component are open: "Figma is ahead: 2 changes being
395
+ * built (Ana)." Null when none is open (it disappears once shipped).
396
+ */
397
+ export declare function figmaAheadLine(views: readonly Pick<BuildRequestView, 'component' | 'status' | 'builders'>[], component: string, nameOf?: (login: string) => string): {
398
+ count: number;
399
+ builders: string[];
400
+ sentence: string;
401
+ } | null;
402
+ /** "Button: add a lg size", the issue title of a build request (same rule as every request). */
403
+ export declare const buildRequestTitle: (r: Pick<BuildRequestInput, "component" | "title">) => string;
@@ -0,0 +1,102 @@
1
+ export type ChangelogBump = 'major' | 'minor' | 'patch';
2
+ export interface ChangelogEntry {
3
+ source: 'changeset' | 'release';
4
+ /** First sentence of the note, the row title. */
5
+ title: string;
6
+ /** Rest of the note's first paragraph, may be empty. */
7
+ detail: string;
8
+ kind: string;
9
+ bump: ChangelogBump | null;
10
+ packages: string[];
11
+ forYou: string;
12
+ /** The author wrote the "For you:" line (forYou is not the default for the bump). */
13
+ forYouAuthored?: boolean;
14
+ date?: string;
15
+ /** Changeset id: the commit sha prefix in a CHANGELOG.md, or the .changeset file path for pending ones. */
16
+ ref?: string;
17
+ }
18
+ export interface ChangelogReleasePackage {
19
+ name: string;
20
+ version: string;
21
+ /** Published to npm (not a private app). */
22
+ published: boolean;
23
+ }
24
+ export interface ChangelogRelease {
25
+ /** "v1.1.0" (the Library version, from @bridge-ds/react) or "core 0.3.1" when react is not in the release. */
26
+ name: string;
27
+ date?: string;
28
+ pending: boolean;
29
+ packages: ChangelogReleasePackage[];
30
+ entries: ChangelogEntry[];
31
+ announcement: string;
32
+ }
33
+ /** One "## x.y.z" section of a package CHANGELOG.md. */
34
+ export interface ChangelogSection {
35
+ pkg: string;
36
+ version: string;
37
+ published: boolean;
38
+ notes: {
39
+ id: string;
40
+ bump: ChangelogBump;
41
+ text: string;
42
+ }[];
43
+ }
44
+ export declare const shortPackage: (name: string) => string;
45
+ /** Splits a paragraph into sentences: ". ", "! " or "? " before a capital, a quote or a backtick, never inside `code`. */
46
+ export declare function noteSentences(para: string): string[];
47
+ /** Longest second sentence shown under a title; a longer one stays in the full notes rather than being cut. */
48
+ export declare const CHANGELOG_DETAIL_MAX = 240;
49
+ /**
50
+ * A note as a row: its first sentence is the title (no trailing period), the second one the description when it is
51
+ * short enough to read in a list, and the author's "For you:" line when there is one.
52
+ */
53
+ export declare function splitNote(text: string): {
54
+ title: string;
55
+ detail: string;
56
+ forYou?: string;
57
+ };
58
+ /** Parses a package CHANGELOG.md into its version sections. "Updated dependencies" lines are not changes. */
59
+ export declare function parseChangelogSections(md: string, pkg: string, published: boolean): ChangelogSection[];
60
+ /** One pending .changeset/*.md file, or null when it is not a changeset. */
61
+ export declare function parseChangeset(src: string, ref: string): ChangelogEntry | null;
62
+ /**
63
+ * Groups version sections into releases. Sections released together share a date, or, when no date is known
64
+ * (shallow clone, no tags), share a changeset: the release action writes one changeset into every package it
65
+ * bumps. Each changeset shows once per release, with every package it touched. Newest first.
66
+ * `dateOf(pkg, version)` returns the release date (YYYY-MM-DD) or undefined; it never guesses.
67
+ */
68
+ export declare function groupReleases(sections: ChangelogSection[], dateOf: (pkg: string, version: string) => string | undefined, libraryPackage?: string): Omit<ChangelogRelease, 'announcement'>[];
69
+ /** Breaking first, then New, Fix, Internal; the notes' own order inside a kind. */
70
+ export declare function sortChangelogEntries<T extends {
71
+ kind: string;
72
+ }>(entries: T[]): T[];
73
+ export declare function releaseAnnouncement(r: Omit<ChangelogRelease, 'announcement'>): string;
74
+ /** Meta line of an entry: the bump in words, then the packages (short names). Over `max`, only a count. */
75
+ export declare function releasePackagesLabel(packages: string[], max?: number): {
76
+ tags: string[];
77
+ count: string | null;
78
+ };
79
+ /** A release as the snapshot carries it: the announcement is left out (releaseAnnouncement(r) rebuilds it). */
80
+ export type ChangelogReleaseRow = Omit<ChangelogRelease, 'announcement'>;
81
+ export interface CollectReleasesInput {
82
+ /** Each package's CHANGELOG.md text, with its npm name and whether it is published (not private). */
83
+ changelogs: {
84
+ pkg: string;
85
+ published: boolean;
86
+ text: string;
87
+ }[];
88
+ /** Pending .changeset/*.md files: `ref` is the file path. README.md is not a changeset and is skipped by the caller. */
89
+ changesets?: {
90
+ ref: string;
91
+ text: string;
92
+ }[];
93
+ /** Release date (YYYY-MM-DD) of a package version, or undefined; never guessed. */
94
+ dateOf?: (pkg: string, version: string) => string | undefined;
95
+ /** Package whose version names a release ("v1.1.0"). */
96
+ libraryPackage?: string;
97
+ }
98
+ /**
99
+ * Every release of a repository, newest first: the pending changesets as "Coming in the next release" when there are
100
+ * any, then the published versions grouped by groupReleases. What the Changelog shows and the team snapshot carries.
101
+ */
102
+ export declare function collectReleases(input: CollectReleasesInput): ChangelogReleaseRow[];